package com.thudojsc.canvasengine.entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import android.graphics.Canvas;
import android.graphics.Paint;

public class Node {
	/*Node Position*/
	protected float mX;
	protected float mY;
	
	/*Node Size*/
	protected float mWidth;
	protected float mHeight;
	
	/*Children*/
	protected ArrayList<Node> mChildren = new ArrayList<Node>();
	private ArrayList<Node> mRequireRemove = new ArrayList<Node>();
	
	/*ZIndex*/
	private int mZIndex = 0;
	
	/*Parent*/
	protected Node mParent = null;
	
	/*Set visible*/
	private boolean mVisible = true;
	
	/*Paint Helper*/
	protected Paint mPaint;
	
	/*----------CONSTRUCTOR----------*/
	public Node() {
		this.mPaint = new Paint();
		
		mPaint.setAntiAlias(true);
		mPaint.setDither(true);
		mPaint.setFilterBitmap(true);
		
	}
	
	public Node(final float pX, final float pY) {
		this();
		
		this.mX = pX;
		this.mY = pY;
	}
	
	public Node(final float pX, final float pY, final float pWidth, final float pHeight) {
		this(pX, pY);
		
		this.mWidth = pWidth;
		this.mHeight = pHeight;
	}
	
	/*---------------PARENT-----------------------*/
	public Node getParent() {
		return mParent;
	}
	
	/*---------------POSITION AND SIZE------------*/
	public void setPosition(final float pX, final float pY) {
		this.mX = pX;
		this.mY = pY;
	}
	
	public float getX() {
		return mX;
	}
	
	public float getY() {
		return mY;
	}
	
	public void setSize(final float pWidth, final float pHeight) {
		this.mWidth = pWidth;
		this.mHeight = pHeight;
	}
	
	public float getWidth() {
		return mWidth;
	}
	
	public float getHeight() {
		return mHeight;
	}
	
	
	
	/*---------------UPDATE----------------*/
	/**
	 * Update Self
	 * @param dt
	 */
	protected void onUpdate(final float dt) {
		onEnter();
	}
	
	/**
	 * Update Self and Children
	 * @param dt
	 */
	public void update(final float dt) {
		onUpdate(dt);
		/*Remove all children be checked remove*/
		final int remove_count = mRequireRemove.size();
		
		for (int index = 0; index < remove_count; index++) {
			final Node removedChild = mRequireRemove.get(index);
			mChildren.remove(removedChild);
		}
		
		mRequireRemove.clear();
		
		/*Update all children*/
		Collections.sort(mChildren, mZIndexComparator);
		
		final int child_count = mChildren.size();
		
		for (int index = 0; index < child_count; index++) {
			final Node child = mChildren.get(index);
			child.update(dt);
		}
	}
	
	/*-----------------DRAW-----------------*/
	/**
	 * Draw Self
	 * @param pCanvas
	 */
	protected void onDraw(final Canvas pCanvas) {
	}
	
	/**
	 * Draw Self and Children
	 * @param pCanvas
	 */
	public void draw(final Canvas pCanvas) {
		final float pTranslateX = mX;
		final float pTranslateY = mY;
		
		//Move canvas to relative position
		pCanvas.translate(pTranslateX, pTranslateY);
		
		onDraw(pCanvas);
		
		final int child_count = mChildren.size();
		
		for (int index = 0; index < child_count; index++) {
			
			final Node child = mChildren.get(index);
			
			if (child.mVisible)
				child.draw(pCanvas);
		}
		
		//Return last position
		pCanvas.translate(-pTranslateX, -pTranslateY);
	}
	
	/*-----------------CHILDREN-------------*/
	
	public void addChild(final Node child, final int zIndex) {
		child.mZIndex = zIndex;
		child.mParent = this;
		
		int index = Collections.binarySearch(mChildren, child, mZIndexComparator);
		
		if (index >= 0) {
			final int child_count = mChildren.size();
			
			Node prevChild;
			
			do {
				prevChild = mChildren.get(index);
				index++;
			} while (index < child_count && mChildren.get(index).mZIndex == prevChild.mZIndex);
			
		} else {
			index = -(index + 1);
		}
		
		mChildren.add(index, child);
		
	}
	
	public void addChild(final Node pChild) {
		addChild(pChild, pChild.mZIndex);
	}
	
	public void reorderZIndex(final Node pChild, final int pZIndex) {
		mChildren.remove(pChild);
		addChild(pChild, pZIndex);
	}
	
	public void removeChild(final Node pChild) {
		pChild.mParent = null;
		mRequireRemove.add(pChild);
	}
	
	public List<Node> getChildren() {
		return this.mChildren;
	}
	
	/*------------------ZINDEX--------------------*/
	public int getZIndex() {
		return this.mZIndex;
	}
	
	/*------------------------LOCATION----------------------*/
	/*
	public void convertLocalToScreenCoordinate(final int localX, final int localY, final int[] screenCoordinate) {
		int screenX = localX;
		int screenY = localY;
		
		Node curNode = this;
		
		while (curNode != null) {
			screenX += curNode.mX;
			screenY += curNode.mY;
			curNode = curNode.mParent;
		}
		
		screenCoordinate[0] = screenX;
		screenCoordinate[1] = screenY;
	}
	
	
	public void convertScreenToLocalCoordinate(final int screenX, final int screenY, final int[] localCoordinate) {
		int localX = screenX;
		int localY = screenY;
		
		Node 	curNode = this;
		
		while (curNode != null) {
			localX -= curNode.mX;
			localY -= curNode.mY;
			curNode = curNode.mParent;
		}
		
		localCoordinate[0] = localX;
		localCoordinate[1] = localY;
	}
	*/
	
	/*Visible*/
	public void setVisible(final boolean pVisible) {
		this.mVisible = pVisible;
	}
	
	public boolean getVisible() {
		return mVisible;
	}
	
	/*Draw Helper*/
	public void setAlpha(final int pAlpha) {
		mPaint.setAlpha(pAlpha);
	}
	
	public int getAlpha() {
		return mPaint.getAlpha();
	}
	
	public void setColor(final int pColor) {
		mPaint.setColor(pColor);
	}
	
	public int getColor() {
		return mPaint.getColor();
	}
	//////////////////EXTENSION////////////////////
	private Comparator<Node> mZIndexComparator = new Comparator<Node>() {
		
		@Override
		public int compare(Node lhs, Node rhs) {
			// TODO Auto-generated method stub
			return lhs.mZIndex - rhs.mZIndex;
		}
	};
	
	protected void onEnter() {
		
	}
	
	public void onScreenSizeChanged() {
		
	}
	
}
